package com.huslee.shark.alert.handler;

import com.alibaba.fastjson.JSON;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.huslee.shark.alert.entity.AlertInfo;
import com.huslee.shark.alert.enums.AlertTypeEnum;
import com.huslee.shark.alert.entity.NoticeItem;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

/**
 * @Desc
 * @Author lishuai
 * @Create 2021/1/18
 */
public class RequestControl {
    private static final int LINESIZE= 5;
    private static final String SPLITER = "----";
    private static final ConcurrentHashMap<Long, List<NoticeItem>> requests = new ConcurrentHashMap<>();

    public static void exec(AlertInfo alertInfo, String webhook, String secret) {
        Long timestamp = System.currentTimeMillis()/10000;
        if(!requests.containsKey(timestamp)){
            requests.put(timestamp, new ArrayList<>());
        }
        requests.get(timestamp).add(new NoticeItem(alertInfo, webhook, secret));
    }

    public static void execSyn(AlertInfo alertInfo, String webhook, String secret) {
        OapiRobotSendRequest request = null;
        if(AlertTypeEnum.TEXT.getType().equals(alertInfo.getType())){
            StringBuilder stringBuilder = new StringBuilder();
            if(CollectionUtils.isNotEmpty(alertInfo.getContents())){
                for(String s : alertInfo.getContents()){
                    stringBuilder.append(s).append("\n");
                }
            }
            if(alertInfo.getE() != null){
                stringBuilder.append(getExceptionStackTrace(alertInfo.getE(), LINESIZE));
            }
            request = text(alertInfo.getAts(), stringBuilder.toString());
        }else if(AlertTypeEnum.LINK.getType().equals(alertInfo.getType())){
            request = link(alertInfo.getAts(), alertInfo.getTitle(), alertInfo.getContents(), alertInfo.getMessageUrl(), alertInfo.getPicUrl(), alertInfo.getE());
        }else if(AlertTypeEnum.MARKDOWN.getType().equals(alertInfo.getType())){
            StringBuilder stringBuilder = new StringBuilder();

            if(StringUtils.isNotBlank(alertInfo.getTitle())){
                stringBuilder.append(alertInfo.getTitle()).append("\n\n");
            }

            if(CollectionUtils.isNotEmpty(alertInfo.getContents())){
                for(String s : alertInfo.getContents()){
                    stringBuilder.append(s).append("\n\n");
                }
            }

            if(alertInfo.getE() != null){
                stringBuilder.append(getExceptionStackTrace(alertInfo.getE(), LINESIZE)).append("\n\n");
            }

            if(StringUtils.isNotBlank(alertInfo.getMessageUrl())){
                stringBuilder.append(alertInfo.getMessageUrl()).append("\n\n");
            }

            request = markdown(alertInfo.getAts(), alertInfo.getTitle(), stringBuilder.toString());
        }

        if(request != null){
            request(webhook, secret, request);
        }
    }

    private static void request(String webhook, String secret, OapiRobotSendRequest request){
        try{
            Long timestampReq = System.currentTimeMillis();
            String stringToSign = timestampReq + "\n" + secret;
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
            String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8");

            DingTalkClient client = new DefaultDingTalkClient(String.format("%s&timestamp=%d&sign=%s", webhook, timestampReq, sign));
            client.execute(request);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void exec(ExecutorService executor){
        Long timestamp = (System.currentTimeMillis()/10000) - 1;
        if(CollectionUtils.isEmpty(requests.get(timestamp))){
            return;
        }
        List<NoticeItem> noticeItems = requests.remove(timestamp);
        Set<String> md5s = new HashSet<>();
        Map<String, List<NoticeItem>> texts = new HashMap<>();
        Map<String, NoticeItem> links = new HashMap<>();
        Map<String, List<NoticeItem>> markdowns = new HashMap<>();
        for(NoticeItem noticeItem : noticeItems){
            String notice = JSON.toJSONString(noticeItem);
            if(md5s.contains(notice)){
               continue;
            }
            md5s.add(notice);

            if(AlertTypeEnum.TEXT.getType().equals(noticeItem.getAlertInfo().getType())){
                if(!texts.containsKey(noticeItem.getWebhook())){
                    texts.put(noticeItem.getWebhook(), new ArrayList<>());
                }
                texts.get(noticeItem.getWebhook()).add(noticeItem);
            }else if(AlertTypeEnum.LINK.getType().equals(noticeItem.getAlertInfo().getType())){
                links.put(noticeItem.getWebhook(), noticeItem);
            }else if(AlertTypeEnum.MARKDOWN.getType().equals(noticeItem.getAlertInfo().getType())){
                if(!markdowns.containsKey(noticeItem.getWebhook())){
                    markdowns.put(noticeItem.getWebhook(), new ArrayList<>());
                }
                markdowns.get(noticeItem.getWebhook()).add(noticeItem);
            }
        }

        List<RequestData> requestDatas = new ArrayList<>();
        assembleText(requestDatas, texts);
        assembleLink(requestDatas, links);
        assembleMarkdown(requestDatas, markdowns);

        executor.execute(() -> {
            for(RequestData requestData : requestDatas){
                request(requestData.getWebhook(), requestData.getSecret(), requestData.getRequest());
            }
        });
    }
    private static void assembleText(List<RequestData> requestDatas, Map<String, List<NoticeItem>> texts){
        for(String webhook : texts.keySet()){
            String secret = null;
            List<String> ats = new ArrayList<>();
            StringBuilder stringBuilder = new StringBuilder();
            for(NoticeItem noticeItem : texts.get(webhook)){
                secret = noticeItem.getSecret();

                AlertInfo alertInfo = noticeItem.getAlertInfo();
                if(CollectionUtils.isNotEmpty(alertInfo.getAts())){
                    ats.addAll(alertInfo.getAts());
                }

                if(CollectionUtils.isNotEmpty(alertInfo.getContents())){
                    for(String s : alertInfo.getContents()){
                        stringBuilder.append(s).append("\n");
                    }
                }
                if(alertInfo.getE() != null){
                    stringBuilder.append(getExceptionStackTrace(alertInfo.getE(), LINESIZE));
                }
                stringBuilder.append(SPLITER).append("\n");
            }
            requestDatas.add(new RequestData(webhook, secret, text(ats, stringBuilder.toString())));
        }
    }

    private static void assembleLink(List<RequestData> requestDatas, Map<String, NoticeItem> links){
        for(String webhook : links.keySet()){
            AlertInfo alertInfo = links.get(webhook).getAlertInfo();
            requestDatas.add(new RequestData(
                    webhook,
                    links.get(webhook).getSecret(),
                    link(alertInfo.getAts(), alertInfo.getTitle(), alertInfo.getContents(), alertInfo.getMessageUrl(), alertInfo.getPicUrl(), alertInfo.getE())
            ));
        }
    }

    private static void assembleMarkdown(List<RequestData> requestDatas, Map<String, List<NoticeItem>> markdowns){
        for(String webhook : markdowns.keySet()){
            String secret = null;
            List<String> ats = new ArrayList<>();
            StringBuilder stringBuilder = new StringBuilder();
            for(NoticeItem noticeItem : markdowns.get(webhook)){
                secret = noticeItem.getSecret();

                AlertInfo alertInfo = noticeItem.getAlertInfo();
                if(CollectionUtils.isNotEmpty(alertInfo.getAts())){
                    ats.addAll(alertInfo.getAts());
                }

                if(StringUtils.isNotBlank(alertInfo.getTitle())){
                    stringBuilder.append(alertInfo.getTitle()).append("\n\n");
                }

                if(CollectionUtils.isNotEmpty(alertInfo.getContents())){
                    for(String s : alertInfo.getContents()){
                        stringBuilder.append(s).append("\n\n");
                    }
                }

                if(alertInfo.getE() != null){
                    stringBuilder.append(getExceptionStackTrace(alertInfo.getE(), LINESIZE)).append("\n\n");
                }

                if(StringUtils.isNotBlank(alertInfo.getMessageUrl())){
                    stringBuilder.append(alertInfo.getMessageUrl()).append("\n\n");
                }

                stringBuilder.append(SPLITER).append("\n\n");
            }
            requestDatas.add(new RequestData(webhook, secret, markdown(ats, null, stringBuilder.toString())));
        }
    }


    private static OapiRobotSendRequest text(List<String> ats, String content){
        OapiRobotSendRequest request = new OapiRobotSendRequest();

        request.setMsgtype(AlertTypeEnum.TEXT.getType());

        OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
        text.setContent(content);
        request.setText(text);

        OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
        at.setAtMobiles(ats);
        at.setIsAtAll(false);
        request.setAt(at);

        return request;
    }

    private static OapiRobotSendRequest link(List<String> ats, String title, List<String> contents, String messageUrl, String picUrl, Exception e){
        OapiRobotSendRequest request = new OapiRobotSendRequest();

        request.setMsgtype(AlertTypeEnum.LINK.getType());

        OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();
        link.setMessageUrl(messageUrl);
        link.setPicUrl(picUrl);
        link.setTitle(title);
        StringBuilder stringBuilder = new StringBuilder();
        for(String s : contents){
            stringBuilder.append(s).append("\n\n");
        }
        stringBuilder.append(getExceptionStackTrace(e, LINESIZE));
        link.setText(stringBuilder.toString());
        request.setLink(link);

        OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
        at.setAtMobiles(ats);
        at.setIsAtAll(false);
        request.setAt(at);

        return request;
    }

    private static OapiRobotSendRequest markdown(List<String> ats, String title, String content){
        title = StringUtils.isBlank(title) ? "[报警信息]": title;

        OapiRobotSendRequest request = new OapiRobotSendRequest();
        request.setMsgtype(AlertTypeEnum.MARKDOWN.getType());

        OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
        markdown.setTitle(title);
        markdown.setText(content);
        request.setMarkdown(markdown);

        OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
        at.setAtMobiles(ats);
        at.setIsAtAll(false);
        request.setAt(at);

        return request;
    }


    private static String getExceptionStackTrace(Exception e, int lineSize){
        if(e == null){
            return "";
        }

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(" > ###### ").append(e.getClass().getName()).append("  ").append(e.getMessage()).append("\n");
        for(int i = 0; i < e.getStackTrace().length && i < lineSize; i++){
            stringBuilder.append(" > ###### ").append(e.getStackTrace()[i]).append("\n");
        }

        return stringBuilder.toString();
    }

    @Data
    private static class RequestData{
        private String webhook;
        private String secret;
        private OapiRobotSendRequest request;

        public RequestData(String webhook, String secret, OapiRobotSendRequest request){
            this.webhook = webhook;
            this.secret = secret;
            this.request = request;
        }

    }
}
